'use strict';

const socketIOClient = require('socket.io-client');

/**
 * RPC客户端，用于远程调用服务
 * 支持延迟连接的模式
 */
class RpcClient {
  /**
   * 构造函数
   * @param {string} serviceName 服务名称
   * @param {number} port 端口号
   * @param {string} host 主机地址（默认为localhost）
   */
  constructor(serviceName, port, host = 'localhost') {
    this.serviceName = serviceName;
    this.port = port;
    this.host = host;
    this.socket = null;
    this.isConnecting = false;
    this.pendingRequests = [];
    this._isDebug = process.env.NODE_ENV !== 'production';

    // 创建代理对象
    return this._createProxy();
  }

  /**
   * 创建代理对象
   * @returns {Proxy} 代理对象
   * @private
   */
  _createProxy() {
    const client = this;

    return new Proxy({}, {
      get(target, method) {
        // 处理特殊方法
        if (method === 'connect') {
          return () => client.connect();
        } else if (method === 'disconnect') {
          return () => client.disconnect();
        } else if (method === 'isConnected') {
          return () => client.socket && client.socket.connected;
        }
        // 对于其他方法，返回一个调用函数
        return async (...args) => {
          // 判断 method 中是否有 "."
          let handlerName = client.serviceName, methodName = method;
          if (method.includes('.')) {
            [handlerName, methodName] = method.split('.');
          }
          // 特殊处理Logger服务的调用
          if (client.serviceName === 'Logger') {
            const callerServiceName = process.env.SERVICE_NAME || 'Main';
            const callerPort = process.env.SERVICE_PORT || '3000';
            // 在日志消息前添加调用者信息
            args = [callerServiceName, callerPort, ...args];
          }
          await client.ensureConnected();
          return client.invokeMethod(handlerName, methodName, args);
        };
      }
    });
  }

  /**
   * 确保连接已建立
   * @returns {Promise<void>}
   */
  async ensureConnected() {
    // 已连接，直接返回
    if (this.socket && this.socket.connected) {
      return;
    }

    // 已有连接过程，等待连接完成
    if (this.isConnecting) {
      return new Promise(resolve => {
        const checkInterval = setInterval(() => {
          if (this.socket && this.socket.connected) {
            clearInterval(checkInterval);
            resolve();
          }
        }, 100);

        // 设置超时
        setTimeout(() => {
          clearInterval(checkInterval);
          if (this.socket && this.socket.connected) {
            resolve();
          } else {
            resolve(); // 即使连接失败也resolve，让后续请求重试
          }
        }, 5000);
      });
    }

    // 建立新连接
    return this.connect();
  }

  /**
   * 建立连接
   * @returns {Promise<boolean>} 是否连接成功
   */
  async connect() {
    if (this.socket && this.socket.connected) {
      return true;
    }

    this.isConnecting = true;

    try {
      const url = `http://${this.host}:${this.port}`;

      if (this._isDebug) {
        console.log(`[RpcClient] 正在连接到 ${this.serviceName} 服务，地址: ${url}`);
      }

      // 创建Socket.IO客户端
      this.socket = socketIOClient(url, {
        reconnection: true,
        reconnectionDelay: 1000,
        reconnectionDelayMax: 5000,
        reconnectionAttempts: Infinity,
        transports: ['websocket']
      });

      // 等待连接建立
      const connected = await new Promise((resolve) => {
        let resolved = false;

        // 设置连接超时
        const timeout = setTimeout(() => {
          if (!resolved) {
            resolved = true;
            if (this._isDebug) {
              console.warn(`[RpcClient] 连接 ${this.serviceName} 服务超时，地址: ${url}`);
            }
            resolve(false);
          }
        }, 5000);

        // 连接成功
        this.socket.on('connect', () => {
          if (!resolved) {
            resolved = true;
            clearTimeout(timeout);
            if (this._isDebug) {
              console.log(`[RpcClient] 已连接到 ${this.serviceName} 服务，地址: ${url}`);
            }
            resolve(true);
          }
        });

        // 连接失败
        this.socket.on('connect_error', (err) => {
          if (!resolved) {
            resolved = true;
            clearTimeout(timeout);
            if (this._isDebug) {
              console.error(`[RpcClient] 连接 ${this.serviceName} 服务失败，地址: ${url}`, err);
            }
            resolve(false);
          }
        });
      });

      // 处理断开连接事件
      this.socket.on('disconnect', () => {
        if (this._isDebug) {
          console.log(`[RpcClient] 与 ${this.serviceName} 服务断开连接，地址: ${url}`);
        }
      });

      // 处理错误事件
      this.socket.on('error', (error) => {
        console.error(`[RpcClient] 与 ${this.serviceName} 服务通信出错:`, error);
      });

      // 完成连接过程
      this.isConnecting = false;

      // 处理等待中的请求
      this._processPendingRequests();

      return connected;
    } catch (error) {
      this.isConnecting = false;
      console.error(`[RpcClient] 连接 ${this.serviceName} 服务异常:`, error);
      return false;
    }
  }

  /**
   * 断开连接
   */
  disconnect() {
    if (this.socket) {
      this.socket.disconnect();
      this.socket = null;
    }
  }

  /**
   * 调用远程方法
   * @param {string} handler 处理器名称
   * @param {string} method 方法名
   * @param {Array} args 参数
   * @returns {Promise<any>} 返回结果
   */
  async invokeMethod(handler, method, args) {
    if (!this.socket || !this.socket.connected) {
      // 尝试重新连接
      const connected = await this.connect();
      if (!connected) {
        throw new Error(`无法连接到 ${this.serviceName} 服务，无法调用方法: ${method}`);
      }
    }

    return new Promise((resolve, reject) => {
      // 调用远程方法
      this.socket.emit('invoke', {
        serviceName: this.serviceName,
        handlerName: handler,
        methodName: method,
        args: args
      }, (response) => {
        if (response.success) {
          // 如果有 result 字段，返回 result；否则返回整个 response
          resolve('result' in response ? response.result : response);
        } else {
          reject(new Error(response.error || '调用失败'));
        }
      });
    });
  }

  /**
   * 处理等待中的请求
   * @private
   */
  _processPendingRequests() {
    if (!this.socket || !this.socket.connected || this.pendingRequests.length === 0) {
      return;
    }

    const requests = [...this.pendingRequests];
    this.pendingRequests = [];

    for (const req of requests) {
      this.invokeMethod(req.handler, req.method, req.args)
        .then(req.resolve)
        .catch(req.reject);
    }
  }
}

module.exports = RpcClient; 